16.2. 输入/输出(printf 和 scanf)

C 的 printf 函数将值打印到终端,而 scanf 函数读取用户输入的值。printfscanf 函数属于 C 的标准 I/O 库,需要通过使用 #include <stdio.h> 明确将其包含在使用这些函数的任何 .c 文件的顶部。在本节中,我们介绍在 C 程序中使用 printfscanf 的基础知识。第 2 章中的“I/O”部分更详细地讨论了 C 的输入和输出函数。

16.2.1. printf

C 的 printf 函数与 Java 中的 System.out.printf 方法非常相似,其中调用者指定要打印的格式字符串。格式字符串通常包含格式说明符,例如将打印制表符(“\t”)或换行符(“\n”)的特殊字符,或输出中值的占位符。占位符由 % 后跟类型说明符字母组成(例如,%d 表示整数值的占位符)。对于格式字符串中的每个占位符,printf 都需要一个额外的参数。表 1包含一个带有格式化输出的 Java 和 C 示例程序:

表 1. Java 和 C 中打印的语法比较

Java versionC version
/* Java formatted print example */

class PrintfExample {

public static void main(String[] args){

System.out.printf("Name: %s, Info:\n",
"Vijay");
System.out.printf("\tAge: %d\t Ht: %g\n",
20, 5.9);
System.out.printf("\tYr: %d\t Dorm: %s\n",
3, "Alice Paul");
}

}
/* C printf example */

#include <stdio.h> // for printf

int main(void) {

printf("Name: %s, Info:\n",
"Vijay");
printf("\tAge: %d\t Ht: %g\n",
20, 5.9);
printf("\tYr: %d\t Dorm: %s\n",
3, "Alice Paul");

return 0;
}

运行时,该程序的两个版本都会产生相同格式的输出:

Name: Vijay,  Info:
	Age: 20 	 Ht: 5.9
	Year: 3 	 Dorm: Alice Paul

C 使用与 Java 相同的格式占位符来指定不同类型的值。 上述示例演示了以下格式占位符:

%g:  placeholder for a float (or double) value
%d:  placeholder for a decimal value (int, short, char)
%s:  placeholder for a string value
%c:  placeholder for a char value

当程序员想要打印与特定数字编码相关的 ASCII 字符时,%c 占位符很有用。这是一个 C 代码片段,它将 char 打印为其数值(%d)和字符编码(%c):

// Example printing a char value as its decimal representation (%d)
// and as the ASCII character that its value encodes (%c)

char ch;

ch = 'A';
printf("ch value is %d which is the ASCII value of  %c\n", ch, ch);

ch = 99;
printf("ch value is %d which is the ASCII value of  %c\n", ch, ch);

运行时,程序的输出如下所示:

ch value is 65 which is the ASCII value of  A
ch value is 99 which is the ASCII value of  c

16.2.2. scanf

C 的 scanf 函数表示一种读取用户输入的值(通过键盘)并将其存储在程序变量中的方法。 scanf 函数对用户输入数据的确切格式可能有点挑剔,这意味着它对格式错误的用户输入不是很稳健。在 第 2 章的“I/O”部分,我们讨论了从用户那里读取输入值的更稳健的方法。现在,请记住,如果您的程序由于格式错误的用户输入而陷入无限循环,您可以随时按 CTRL-C 来终止它。

Java 和 C 中读取输入的处理方式不同:Java 创建一个新的 Scanner 对象并使用其方法读取和返回不同类型的值,而 C 使用 scanf 读取由格式字符串参数指定的不同类型的值,并将它们存储在程序变量的内存位置(例如 &num1)。表 2 显示了使用 Java 和 C 读取用户输入值的示例程序:

表 2. Java 和 C 中读取输入值的方法比较

Java versionC version

/* Java input example */

import java.util.Scanner;

class InputExample {

public static void main(String[] args) {

int num1, num2;
Scanner in = new Scanner(System.in);

System.out.print("Enter a number: ");
num1 = in.nextInt();
System.out.print("Enter another: ");
num2 = in.nextInt();

System.out.printf( "%d + %d = %d\n",
num1, num2, (num1+num2) );
}

}
/* C input (scanf) example */

#include <stdio.h>



int main(void) {

int num1, num2;


printf("Enter a number: ");
scanf("%d", &num1);
printf("Enter another: ");
scanf("%d", &num2);

printf("%d + %d = %d\n",
num1, num2, (num1+num2) );

return 0;
}

运行时,两个程序都会读取两个值(这里是 30 和 67):

Enter a number: 30
Enter another: 67
30 + 67 = 97

printf 一样,scanf 采用格式字符串来指定要读入的值的数量和类型(例如,"%d" 指定一个 int 值)。scanf 函数在读入数值时会跳过前导和尾随空格,因此其格式字符串只需包含一系列格式化占位符,格式字符串中的占位符之间通常没有空格或其他格式字符。格式字符串中占位符的参数指定将存储读入值的程序变量的位置。在变量名称前加上 & 运算符会生成该变量在程序内存中的位置 - 即变量的内存地址。第 2 章中的“指针”部分更详细地讨论了 & 运算符。目前,我们仅在 scanf 函数的上下文中使用它。

下面是另一个 scanf 示例,其中格式字符串有两个值的占位符,第一个是 int,第二个是 float

scanf_ex.c

int x;
float pi;

// read in an int value followed by a float value ("%d%g")
// store the int value at the memory location of x (&x)
// store the float value at the memory location of pi (&pi)
scanf("%d%g", &x, &pi);

通过scanf向程序输入数据时,各个数字输入值必须至少用一个空格字符分隔。但是,由于scanf会跳过额外的前导和尾随空格字符(例如空格、制表符和换行符),因此用户可以在每个输入值之前或之后输入任意数量的空格。例如,如果用户在前面的示例中输入以下内容来调用scanf,则scanf将读入 8 并将其存储在x变量中,然后读入 3.14 并将其存储在pi变量中:

 8                   3.14